package com.yxsk.relay.job.component.common.protocol.caller;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.yxsk.relay.job.component.common.exception.remote.RemoteCallException;
import com.yxsk.relay.job.component.common.net.handler.convert.NetMessageConvert;
import com.yxsk.relay.job.component.common.net.serialization.MessageEntitySerializer;
import com.yxsk.relay.job.component.common.protocol.message.BaseRequest;
import com.yxsk.relay.job.component.common.utils.CollectionUtils;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * @Author 11376
 * @CreaTime 2019/6/15 16:41
 * @Description
 */
@Builder
public class HttpRestfulRemoteCaller implements RemoteCaller {

    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    private HttpClientFactory httpClientFactory;

    private NetMessageConvert messageConvert;

    private MessageEntitySerializer serializer;

    @Override
    public <T> T call(BaseRequest request, String url, Class<T> responseType) throws RemoteCallException {
        return this.call(request, null, url, responseType);
    }

    @Override
    public <T> T call(BaseRequest request, Map<String, String> header, String url, Class<T> responseType) throws RemoteCallException {
        String response = this.remoteCall(request, header, url);
        try {
            return StringUtils.isEmpty(response) ? null : JSONObject.parseObject(response, responseType);
        } catch (Exception e) {
            throw new RemoteCallException(e);
        }
    }

    @Override
    public <T> T call(BaseRequest request, String url, TypeReference<T> responseType) throws RemoteCallException {
        return this.call(request, null, url, responseType);
    }

    @Override
    public <T> T call(BaseRequest request, Map<String, String> header, String url, TypeReference<T> responseType) throws RemoteCallException {
        String resp = remoteCall(request, header, url);
        try {
            return JSONObject.parseObject(resp, responseType);
        } catch (Exception e) {
            throw new RemoteCallException(e);
        }
    }

    private String remoteCall(BaseRequest request, Map<String, String> header, String url) throws RemoteCallException {
        try {
            // 请求报文
            byte[] message = null;
            if (request != null) {
                message = this.serializer.serialize(request);
            }

            // 消息转换
            message = this.messageConvert.writeConvert(message);

            HttpPost method = new HttpPost();
            method.addHeader("Content-type", "application/json; charset=utf-8");
            if (CollectionUtils.isNotEmpty(header)) {
                header.entrySet().stream().forEach(entry -> method.addHeader(entry.getKey(), entry.getValue()));
            }

            method.setEntity(new ByteArrayEntity(message));

            method.setURI(new URI(url));

            HttpResponse response = this.httpClientFactory.getHttpClient().execute(method);

            InputStream inputStream = response.getEntity().getContent();

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buff = new byte[256];

            int offset = -1;
            while ((offset = inputStream.read(buff, 0, 256)) != -1) {
                bos.write(buff, 0, offset);
            }
            byte[] resp = this.messageConvert.readConvert(bos.toByteArray());

            if (response == null || HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) {
                // 调用错误
                throw new RemoteCallException("Error response , response code: " + response.getStatusLine().getStatusCode() + ", message: " + new String(resp, DEFAULT_CHARSET));
            }

            return new String(resp, DEFAULT_CHARSET);
        } catch (Exception e) {
            throw new RemoteCallException(e);
        }
    }

    @Override
    public String buildUri(String host, Integer port, String uri) {
        return new StringBuilder("http://").append(host).append(":").append(port).append(uri).toString();
    }

}
